BIP54 "Consensus Cleanup" implementation#99
BIP54 "Consensus Cleanup" implementation#99ajtowns merged 23 commits intobitcoin-inquisition:29.xfrom
Conversation
ee557fc to
bc95a67
Compare
bc95a67 to
8d513a0
Compare
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
ariard
left a comment
There was a problem hiding this comment.
Just going over the introduced consensus code fix by fix for now.
98a9a5e to
224ef91
Compare
|
Thanks for kicking CI @ajtowns. I fixed the missing annotations that |
224ef91 to
5e06850
Compare
instagibbs
left a comment
There was a problem hiding this comment.
Focused primarily on BIP compliance, everything looks good to me
ariard
left a comment
There was a problem hiding this comment.
Did a first review on the 64-byte fix, answering opened comments.
|
The consensus changes, with respect to their specification in BIP54, look good to me:
I think this PR is well organised (starting at ---). Each consensus change is first prepared, done in a minimal commit, and then followed by new tests. I'll study the test changes in more detail later. |
babef47 to
19d159e
Compare
ariard
left a comment
There was a problem hiding this comment.
See second comment on nSequence == SEQUENCE_FINAL`.
|
Finished a first round of review of the code for the 4 consensus fixes:
Will review more the fixes at the conceptual level and the breadth / depth of their respective test coverage. |
Sjors
left a comment
There was a problem hiding this comment.
See inline suggestion for renaming the bip_active argument to check_bip54 and making it a bit more clear in the tests if they're checking standardness (pre activation) or consensus (post activation).
The other consensus commit changes for the 19d159e push (compared to my previous review) look fine.
src/policy/policy.cpp
Outdated
| return true; // Coinbases don't use vin normally | ||
| } | ||
|
|
||
| if (!Consensus::CheckSigopsBIP54(tx, mapInputs)) { |
There was a problem hiding this comment.
In d1d435f validation: make BIP54 sigops check consensus-critical: note to other reviewers: this doesn't prematurely drop the standardness check before activation.
There was a problem hiding this comment.
This does, however, make the BIP54 checks mandatory for the mempool, such that they cannot be disabled by acceptnonstdtxn, aiui. (Which is good: you don't want consensus-invalid txs in your mempool at the point the consensus change activates)
This encapsulates the soft fork configuration logic as set by the `-testactivationheight` (for buried deployments) and `-vbparams` (for version bits deployments) options which for the moment are regtest-only, in order to make them available on other networks as well in the next commit. Can be reviewed using git's --color-moved option.
19d159e to
f24256f
Compare
The fuzz target was specifically crafted to support seeding it with the BIP54 test vectors generated by the unit test in the previous commit.
We are going to introduce the timewarp fix for mainnet with a greater grace period. Rename the MAX_TIMEWARP value for testnet to differentiate them. -BEGIN VERIFY SCRIPT- for f in $(git grep -l MAX_TIMEWARP); do sed -i "s/MAX_TIMEWARP/MAX_TIMEWARP_TESTNET4/g" "$f"; done -END VERIFY SCRIPT-
The test vectors were generated using https://github.com/darosior/bitcoin/tree/bip54_miner
That is, enforce nLockTime be set to height-1 and nSequence not be set to final.
The test vectors were generated using https://github.com/darosior/bitcoin/tree/bip54_miner
… vectors) This adds tests exercising the bounds of the checks on the invalid transaction size, for various types of transactions (legacy, Segwit, bytes in input/output to get to 64 bytes) as well as sanity checking against some known historical violations. Thanks to Chris Stewart for digging up the historical violations to this rule.
It's not a standardness limit anymore, it was made consensus. Thanks to Anthony Towns for the scripted diff script. -BEGIN VERIFY SCRIPT- sed -i 's/MAX_STD_LEGACY_SIGOPS/MAX_TX_BIP54_SIGOPS/g' $(git grep -l MAX_STD_LEGACY_SIGOPS) sed -i 's/signature operations in validating a transaction./signature operations in a single transaction, per BIP54./' test/functional/test_framework/script_util.py -END VERIFY SCRIPT- Co-Authored-by: Anthony Towns <aj@erisian.com.au>
The previously introduced unit tests extensively test the specific implementation of each mitigation. This functional test complements them by end-to-end testing all mitigations. For the added timestamp constraints, it mimicks how they would get exploited (by implementing pseudo timewarp and Murch-Zawy attacks) and demonstrates those exploits are not possible anymore after BIP54 activates.
0d298f5 to
55cd9a5
Compare
Would simply adding the block's coinbase's locktime to the output of verbosity-1 diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index bbe401fcc6c..78062c74e6f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -183,6 +183,7 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
result.pushKV("strippedsize", (int)::GetSerializeSize(TX_NO_WITNESS(block)));
result.pushKV("size", (int)::GetSerializeSize(TX_WITH_WITNESS(block)));
result.pushKV("weight", (int)::GetBlockWeight(block));
+ result.pushKV("cblocktime", block.vtx.at(0)->nLockTime);
UniValue txs(UniValue::VARR);
switch (verbosity) {
@@ -724,6 +725,7 @@ static RPCHelpMan getblock()
{RPCResult::Type::NUM, "version", "The block version"},
{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
{RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
+ {RPCResult::Type::NUM, "locktime", "The coinbase transaction's locktime value"},
{RPCResult::Type::ARR, "tx", "The transaction ids",
{{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},I've tested this on mainnet with your snippet from earlier and it's reasonably performant. |
|
On the coinbase transaction and requesting the nLocktime to be set to the current block height minus one and the nSequence field must not be equal to 0xff_ff_ff_ff, I'm still thinking the second check is unecessary. The first check is ensuring that the transaction is always final (2nd check of https://groups.google.com/g/bitcoindev/c/6TTlDwP2OQg Per se, unless I'm missing something, I don't think the value of the nSequence field needs to be set to a precise value and the whole field could be even “free" up field either for (a) for an extra nonce or (b) as a space where to fit a new commitment structure in the future (and that's particularly interesting as it would work even if there are no segwit txn in the block cf. BIP141). cc @darosior |
|
|
||
| // Make sure the coinbase transaction is timelocked to the block's height. | ||
| if (nHeight > 0 && DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_CONSENSUSCLEANUP)) { | ||
| Assert(!block.vtx.empty() && block.vtx[0] && !block.vtx[0]->vin.empty()); |
There was a problem hiding this comment.
This is not consensus, but more relay. At CMPCTBLOCK reception, there must be at least one transaction.
if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty()))
return READ_STATUS_INVALID;
Sanitizing that the received coinbase is respecting the new rules of consensus avoids to store cmpct block state for a block that will be overall consenus invalid (note the validation of the coinbase is already lax afaict).
There was a problem hiding this comment.
That would be a tiny optimisation in the event that someone decided to waste an enormous amount of energy creating an invalid block. I don't think it's worth it.
There was a problem hiding this comment.
The number of compact block relay peers != block relay peers iirc, so I think there still some margin of shitting the coinbase tx and playing with it, but effectively I don’t think it’s something per se new coming with BIP54, and it’s rather ante date it.
Yeah, seems like -- it takes about 3 times as long as the dedicated RPC for me in a quick test, but that's good enough for how much simpler it is. I found "cblocktime" confusing ( I messed around with the dedicated approach some more; it looks like if you add a dedicated
|
Indeed, it reads like "c block time",
Ideally we keep it generic enough so it can be PR'd to Bitcoin Core indepedent of BIP54. Maybe add a Update: bitcoin#34512 |
|
ACK 55cd9a5 -- this looks good to me i think, and should be ready to merge |
@ariard i have replied to you on the mailing list. TL;DR: it seems my explanation was lacking and you missed the purpose for making sure nSequence is never final.
Note also we don't constrain it to be a specific value. On the contrary it can be anything as long it's not the specific final value. |
@darosior I think I got your idea with the assumption that BIP54 can be buried in the future, and the check to be applied on all historical coinbase transactions, therefore ensuring any old coinbase cannot be used to generate a post-BIP54 action height identifier collision. Replied, I’m still thinking you can make a simpler assumption by just relying on the PoW ordering mechanism itself. There can be only and only one block valid at a given chain height. |
Yes, I see it doesn’t have to be set to a specific value. See the point on protecting the coinbase nSequence field. It’s of independent interest, that the check is kept or not. |
Historical coinbase transactions on mainnet, testnet3, testnet4 and signet all fail to comply with bip54 rules, so that approach isn't possible. |
Okay so up to Darosior what he meant here on the ml: "For instance, if BIP 54 activation height ever gets buried in Bitcoin Core, the BIP 30 check could just be disabled past So strictly a buried activation height, nothing like a retroactive validation from genesis like it was done for BIP30. Point is the additional check in nsequence field is superflous imho. |
|
The issue is what happens if there is a reorg back to a point where neither BIP 34 or BIP 54 are active. At that point, BIP 30 checks must be enabled. With the nsequence check, those checks no longer need to be performed after BIP 54 is enabled because no coinbase that complies with its rules can have ever previously appeared in the blockchain. Also, this discussion should be on the mailing list, not here. |
e0463b4 rpc: add coinbase_tx field to getblock (Sjors Provoost) Pull request description: This adds a `coinbase_tx` field to the `getblock` RPC result, starting at verbosity level 1. It contains only fields guaranteed to be small, i.e. not the outputs. Initial motivation for this was to more efficiently scan for BIP54 compliance. Without this change, it requires verbosity level 2 to get the coinbase, which makes such scan very slow. See bitcoin-inquisition#99 (comment). Adding these fields should be useful in general though and hardly makes the verbosity 1 result longer. ``` bitcoin rpc help getblock getblock "blockhash" ( verbosity ) If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbosity is 1, returns an Object with information about block <hash>. If verbosity is 2, returns an Object with information about block <hash> and information about each transaction. ... Result (for verbosity = 1): { (json object) "hash" : "hex", (string) the block hash (same as provided) "confirmations" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain "size" : n, (numeric) The block size "strippedsize" : n, (numeric) The block size excluding witness data "weight" : n, (numeric) The block weight as defined in BIP 141 "coinbase_tx" : { (json object) Coinbase transaction metadata "version" : n, (numeric) The coinbase transaction version "locktime" : n, (numeric) The coinbase transaction's locktime (nLockTime) "sequence" : n, (numeric) The coinbase input's sequence number (nSequence) "coinbase" : "hex", (string) The coinbase input's script "witness" : "hex" (string, optional) The coinbase input's first (and only) witness stack element, if present }, "height" : n, (numeric) The block height or index "version" : n, (numeric) The block version ... ``` ``` bitcoin rpc getblock 000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6 1 ``` ```json { "hash": "000000000000000000013c986f9aebe800a78454c835ccd07ecae2650bfad3f6", "confirmations": 2, "height": 935113, "version": 561807360, "...": "...", "weight": 3993624, "coinbase_tx": { "version": 2, "locktime": 0, "sequence": 4294967295, "coinbase": "03c9440e04307c84692f466f756e6472792055534120506f6f6c202364726f70676f6c642ffabe6d6d9a8624235259d3680c972b0dd42fa3fe1c45c5e5ae5a96fe10c182bda17080e70100000000000000184b17d3f138020000000000", "witness": "0000000000000000000000000000000000000000000000000000000000000000" }, "tx": [ "70eb053340c7978c5aa1b34d75e1ba9f9d1879c09896317f306f30c243536b62", "5bcf8ed2900cb70721e808b8977898e47f2c9001fcee83c3ccd29e51c7775dcd", "3f1991771aef846d7bb379d2931cccc04e8421a630ec9f52d22449d028d2e7f4", "..." ] } ``` ACKs for top commit: sedited: Re-ACK e0463b4 darosior: re-utACK e0463b4 Tree-SHA512: 1b3e7111e6a0edffde8619c49b3e9bca833c8e90e416defc66811bd56dd00d45b69a84c8fd9715508f4e6515f77ac4fb5c59868ab997ae111017c78c05b74ba3
This implements the BIP54 consensus rules.
Each of the 4 mitigations comes with its own unit test in a new
src/test/bip54_tests.cppmodule. The unit tests can be used to generate JSON test vectors for the BIP (opt-in). For those unit tests that require mainnet blocks, the JSON test vectors were generated separately and included in this PR as data files.Besides the unit test for each mitigation, this contains a fuzz harness for the sigop accounting logic as well as a functional test which goes over all mitigations in pseudo realistic situations (for instance, mimic a Timewarp attack and a Murch-Zawy attack to demonstrate the new timestamp restrictions would prevent that). The fuzz target for the sigop accounting logic was designed to make it possible to seed it from the BIP test vectors. This branch implements that.
The chains of headers for the timestamp restrictions test vectors were generated using this unit test (note the premined headers to allow modifications without re-generating all headers from scratch). The small chains of blocks for the coinbase restrictions test vectors were generated using this unit test.